package dao

import (
	"fmt"
	"osiris/core/conversion"
	"osiris/db"
	"osiris/dto"
	"osiris/logger"
	"osiris/model"
	"time"
)

var (
	currentBlockHeader model.BlockHeaderModel
)

// InitBlock 根据数据库初始化区块链，包括：创世区块检查、更新区块链高度、更新最新区块
//
//	@return error
func InitBlock() error {
	//若没有创世区块则新建创世区块
	var blockChainLength int64
	err1 := db.MySQL.Model(&model.BlockHeaderModel{}).Count(&blockChainLength).Error
	if err1 != nil {
		logger.Error(map[string]interface{}{"[dao] [Init Block] count block": err1})
		return err1
	}

	if blockChainLength == 0 {
		genesisBlock, err2 := genesisBlock()
		if err2 != nil {
			logger.Error(map[string]interface{}{"[dao] [Init Block] genesis block": err2})
			return err2
		}

		err3 := AddBlock(genesisBlock.BlockHeader)
		if err3 != nil {
			logger.Error(map[string]interface{}{"[dao] [Init Block] add genesis block": err3})
			return err3
		}
	}

	//更新区块链高度、最新区块
	err4 := getCurrentBlockInDB()
	if err4 != nil {
		logger.Error(map[string]interface{}{"[dao] [Init] Find current block error": err4})
		return err4
	}

	return nil
}

// genesisBlock 生成创世区块
//
//	@return dto.BlockDto
//	@return error
func genesisBlock() (dto.BlockDto, error) {
	header := dto.BlockHeader{
		Timestamp: time.Now().Unix(),
		//TODO chainID
		ChainID:   0,
		Height:    0,
		ExtraData: map[string]string{"info": "genesis block"},
	}

	var err error
	header.HeaderHash, err = header.Hash()
	if err != nil {
		logger.Error(map[string]interface{}{"[cnosensus] [Genesis Block] calculate header hash": err})
		return dto.BlockDto{}, err
	}

	return dto.BlockDto{
		BlockHeader: header,
	}, nil
}

func GetCurrentHeader() model.BlockHeaderModel {
	return currentBlockHeader
}

// GetHeaderByHash 通过header hash查找区块头
//
//	@param index
//	@return *model.BlockModel
//	@return error
func GetHeaderByHash(headerHash string) (model.BlockHeaderModel, error) {
	var header model.BlockHeaderModel
	err := db.MySQL.Where("headerHash = ?", headerHash).First(&header).Error
	return header, err
}

// GetHeaderByHeight 通过区块高度查找区块头
//
//	@param hash
//	@return *model.BlockModel
//	@return error
func GetHeaderByHeight(height int64) (model.BlockHeaderModel, error) {
	var header model.BlockHeaderModel
	err := db.MySQL.Where("height = ?", height).First(&header).Error
	return header, err
}

// AddBlock
// 验证区块合法性后添加区块，同时更新最新区块和高度；所有加区块的操作都要收敛到这个方法
//
//	@param block BlockDto
//	@return error
func AddBlock(header dto.BlockHeader) error {
	headerModel := conversion.HeaderDto2Model(header)
	err1 := db.MySQL.Create(&headerModel).Error
	if err1 != nil {
		logger.Error(map[string]interface{}{fmt.Sprintf("[dao] [Add Block] height: %d", header.Height): err1})
	}

	currentBlockHeader = headerModel
	logger.Info(map[string]interface{}{fmt.Sprintf("[dao] [Add Block] height: %d", header.Height): "success"})
	return nil
}

func getCurrentBlockInDB() error {
	var headerModel model.BlockHeaderModel
	err1 := db.MySQL.Order("height desc").Limit(1).Find(&headerModel).Error
	if err1 != nil {
		logger.Error(map[string]interface{}{"[dao] [update Current Block]": err1})
	} else {
		logger.Info(map[string]interface{}{"[dao] [update Current Block]": headerModel.Height})
	}

	currentBlockHeader = headerModel

	return err1
}

func TruncateHeaders() error {
	return db.MySQL.Exec(fmt.Sprintf("truncate %s", model.BlockHeaderModel{}.TableName())).Error
}

// SaveBlock Save update value in database, if the value doesn't have primary key, will insert it
//
//	@param block model.BlockModel
//	@return error
func SaveBlock(block model.BlockHeaderModel) error {
	err := db.MySQL.Save(&block).Error
	return err
}

func DeleteBlock(hash string) error {
	err := db.MySQL.Delete(&model.BlockHeaderModel{}, "hash = ?", hash).Error
	return err
}
